package com.ds.lens.data.common.util;

import com.ds.lens.data.common.query.DateHistogramFunction;
import com.ds.lens.data.common.query.DateTimeFunction;
import com.ds.lens.data.common.query.IntervalTimestampVariable;
import com.ds.lens.data.common.query.QueryTimeStampVariable;
import org.springframework.util.CollectionUtils;

import java.util.*;

@SuppressWarnings("unchecked")
public class LensMQLBuilder {

    public static String buildQueryDSL(String metricName, String aggrMethod, String source, Map<String, Object> includes,
                                       Map<String, Object> excludes, List<String> groupBys, TimeRange timeRange,
                                       Long interval, Integer limit) {
        StringBuilder builder = new StringBuilder();
        builder.append("select ")
                .append(aggrMethod)
                .append("(")
                .append(metricName)
                .append(")");
        if (!CollectionUtils.isEmpty(groupBys)) {
            boolean hasNext;
            Iterator<String> itr = groupBys.iterator();
            do {
                hasNext = itr.hasNext();
                if (hasNext) {
                    builder.append(", ");
                    builder.append(itr.next());
                }
            } while (hasNext);
        }
        builder.append(" from ")
                .append(source);
        if (!CollectionUtils.isEmpty(includes) || !CollectionUtils.isEmpty(excludes) || timeRange != null) {
            builder.append(" where 1=1 ");
        }
        boolean f = generateWhereStatement(builder, includes, false, true);
        f = generateWhereStatement(builder, excludes, true, f);
        if (timeRange != null) {
            if (f) {
                builder.append(" and ");
            }
            String queryTimeStamp = QueryTimeStampVariable.get().getText();
            if (timeRange instanceof PastTimeRange) {
                DateTimeFunction dateTimeFunction = DateTimeFunction.get();
                builder.append(queryTimeStamp + " < " + dateTimeFunction.getExpression("m") + " and " +
                        queryTimeStamp + " >= " + dateTimeFunction.getExpression(timeRange.getStartTime() + "m"));
            } else {
                builder.append(queryTimeStamp + " < " + timeRange.getEndTime() + " and " +
                        queryTimeStamp + " >= " + timeRange.getStartTime());
            }
        }
        if (!CollectionUtils.isEmpty(groupBys) || interval != null) {
            builder.append(" group by ");
        }
        f = false;
        if (!CollectionUtils.isEmpty(groupBys)) {
            Iterator<String> itr = groupBys.iterator();
            boolean hasNext;
            do {
                builder.append(itr.next());
                if (hasNext = itr.hasNext()) {
                    builder.append(", ");
                }
            } while (hasNext);
            f = true;
        }
        if (interval != null) {
            if (f) {
                builder.append(", ");
            }
            DateHistogramFunction dateHistogramFunction = DateHistogramFunction.get();
            String expression = dateHistogramFunction.getExpression(IntervalTimestampVariable.get().getText(), interval + "m");
            builder.append(expression);
        }
        if (limit == null || limit <= 0) {
            limit = 100;
        }
        builder.append(" limit " + limit);
        return builder.toString();
    }

    private static boolean generateWhereStatement(StringBuilder builder, Map<String, Object> kvmap, boolean isNot, boolean withAnd) {
        if (!CollectionUtils.isEmpty(kvmap)) {
            if (withAnd) {
                builder.append(" and ");
            }
            Iterator<String> itr = kvmap.keySet().iterator();
            boolean hasNext;
            do {
                String key = itr.next();
                Object value = kvmap.get(key);
                String operator = null;
                if (value instanceof Collection) {
                    Collection c = ((Collection) value);
                    if (c.size() == 1) {
                        operator = isNot ? "!=" : "=";
                        value = formatSingleValue(c.iterator().next());
                    } else {
                        StringBuilder valueBuilder = new StringBuilder();
                        operator = isNot ? "not in" : "in";
                        valueBuilder.append("(");
                        boolean hasNext2;
                        Iterator<Object> itr2 = c.iterator();
                        do {
                            valueBuilder.append(formatSingleValue(itr2.next()));
                            if (hasNext2 = itr2.hasNext()) {
                                valueBuilder.append(",");
                            }
                        } while (hasNext2);
                        valueBuilder.append(")");
                        value = valueBuilder.toString();
                    }
                } else {
                    operator = isNot ? "!=" : "=";
                    value = formatSingleValue(value);
                }
                if (operator != null) {
                    builder.append(key)
                            .append(" ").append(operator).append(" ")
                            .append(value);
                }
                if (hasNext = itr.hasNext()) {
                    builder.append(" and ");
                }
            } while (hasNext);
            return true;
        }
        return withAnd;
    }

    private static String formatSingleValue(Object value) {
        if (value instanceof Number) {
            return value.toString();
        } else {
            return value == null ? null : "'" + value.toString() + "'";
        }
    }

    public static void main(String[] args) {
        Map<String, Object> includes = new HashMap<>();
        includes.put("appId", "xx");
        includes.put("tierId", "xx");
        List<String> groupBys = new ArrayList<>();
        groupBys.add("endpoint");
        String sql = LensMQLBuilder.buildQueryDSL("responseTime", "avg", "integerationId", includes,
                null, groupBys, new PastTimeRange(30l), 5l, 100);
        System.out.println(sql);
    }
}
